package de.suur.jarmi.client;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.suur.jarmi.common.Compressor;
import de.suur.jarmi.common.JarmiUtil;
import de.suur.jarmi.common.Request;
import de.suur.jarmi.common.Response;
import de.suur.jarmi.common.Serializer;

public abstract class JarmiClient {

	private static final transient Logger log = LoggerFactory
			.getLogger(JarmiClient.class);
	private Compressor compressor;
	private Serializer serializer;

	private Class<?> serviceInterface;

	public JarmiClient(Class<?> serviceInterface, Compressor compressor,
			Serializer serializer) {
		this.serviceInterface = serviceInterface;
		this.compressor = compressor;
		this.serializer = serializer;
	}

	protected byte[] receiveResponse(InputStream is) throws IOException {
		byte[] resultSize = new byte[4];
		is.read(resultSize);
		int size = JarmiUtil.byteArrayToInt(resultSize);
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		for (int i = 0; i < size; i++) {
			bos.write(is.read());
		}

		byte[] result = bos.toByteArray();
		bos.close();
		is.close();
		return result;
	}

	protected abstract byte[] request(byte[] requestData);

	private Response request(Request request) {
		byte[] requestData = serializer.serialize(request);
		if (compressor != null) {
			requestData = compressor.compress(requestData);
		}
		byte[] responseData = request(requestData);
		if (compressor != null) {
			responseData = compressor.decompress(responseData);
		}
		if (responseData != null) {
			Response response = serializer.deserialize(responseData);
			return response;
		} else {
			return null;
		}
	}

	public Object request(String methodName, Serializable[] parameters,
			Class<?>[] parameterTypes) {
		Request request = new Request(serviceInterface, methodName, parameters,
				parameterTypes);
		Response response = request(request);
		if (response != null) {
			if (response.isSuccess()) {
				return response.getResult();
			} else {
				throw response.getException();
			}
		}
		return null;
	}

	public <T extends Serializable> void requestAsync(final String methodName,
			final Serializable[] parameters, final Class<?>[] parameterTypes,
			final AsyncCallback<T> callback) {
		new Thread() {
			@Override
			public void run() {
				Request request = new Request(serviceInterface, methodName,
						parameters, parameterTypes);
				Response response = request(request);
				if (response != null) {
					if (response.isSuccess()) {
						callback.onSuccess((T) response.getResult());
					} else {
						callback.onFailure(response.getException());
					}
				} else {
					callback.onFailure(null);
				}
			}
		}.start();
	}

	protected void sendRequest(byte[] requestData, OutputStream os)
			throws IOException {
		log.debug("Sending request size: {}", requestData.length);
		byte[] requestSize = JarmiUtil.intToByteArray(requestData.length);
		os.write(JarmiUtil.JARMI_BYTES);
		os.write(requestSize);
		os.write(requestData);
		os.flush();
	}

}
